package Task20;
import java.util.*;
import java.io.*;
import java.util.regex.Pattern;

//class - component of processing data
public class Model {
	
	static private File workDirectory = new File("C:\\Documents and Settings");
	
	//method for obtaining the required instance of class and create a map
	public static String[] getObject(String[] args) throws NoSuchFieldException, Exception{
		//map is forming once (see the class Factory)
		Factory.form_Map();
		if(Factory.getInstance(args).checkArgs(args)) return (Factory.getInstance(args).doSmth(args));
		else return null;
	}
	
	//method (additional), replace all the '_' on spaces
	//then can used the names of directories, which contain the spaces 
	public static String[] delSpaces(String[] Str){
			String[] modernStr = new String[Str.length];
			for (int i=0;i<Str.length;i++){
				if(!new File(Str[i]).exists()){
				String newstr="";
				for (char s : Str[i].toCharArray())
					if(s=='_') newstr +=' ';
					else newstr += s;
				modernStr[i] = newstr;}
				else modernStr[i] = Str[i];
			}
			return modernStr;
		}

	//class for defined of type the way (absolute or relative)
	private static class wayAbsoluteRelative{
	
		private static String way;
		private static String newWay;
		private static char[] elementsStringWay;
		
		public static String orAbsRel(String Way){
			way = Way;
			if (!way.contains(":")){
				if (!way.contains("..")) wayDown();
				else wayUp();
				}
			else newWay = way;
			return newWay;
			}
		
		private static int getPositionsUp(){
			int countPositions = 0;
			elementsStringWay = way.toCharArray();
			for (int i=0;i<elementsStringWay.length-1;i++)
				if (elementsStringWay[i]=='.')
					if (elementsStringWay[i+1]=='.')
						countPositions++;
			return countPositions;
		}
		
		private static String getSubWay(){
			String subWay;
			int numStartSubWay = 0;
			for (int i=0;i<elementsStringWay.length-1;i++){
				if (elementsStringWay[i]=='.')
					if (elementsStringWay[i+1]=='\\')
					numStartSubWay = i+1;
			}
			if (numStartSubWay!=0) subWay = way.substring(numStartSubWay);
			else subWay = "";
			return subWay;
		}
		
		private static void wayUp(){
			List<Integer> indexes = new LinkedList<Integer>();
			int startIndex = 0;
			int index = 0;
			while(true){
				index = workDirectory.getPath().indexOf('\\',startIndex);
				if (index==-1) break;
				indexes.add(index);
				startIndex = index+1;
				}
			List<String> subWays = new LinkedList<String>();
			for (int i=0;i<indexes.size();i++){
				String subStr = workDirectory.getPath().substring(0,indexes.get(i));
				subWays.add(subStr);
				}
			int numPosition = subWays.size()-getPositionsUp();
			if (numPosition < 0) numPosition = 0;
			newWay = subWays.get(numPosition)+getSubWay();
	}
		
		private static void wayDown(){
			newWay = workDirectory.getPath()+"\\"+way;
	}
	}
	
	public final class printContentDirectory extends modelInterface{
		
		//the public part		
		public static final String key = "dir";
		
		public String[] doSmth(String[] args){ return dir();}
		
		public boolean checkArgs(String[] args){
			if (args.length==1) {
				Directory = workDirectory;
				return true;
			}
			if (args.length==2) {
				Directory = new File(wayAbsoluteRelative.orAbsRel(args[1]));
			return (Directory.exists());
			}
			else return false;
		}
		
		//the private part
		private String[] List;
		private int countFiles,countDirs;
		private File Directory;
		private File[] SubDirectory;
		private long byteCount;
		
		private String[] dir(){
			byteCount=0;
			countFiles=0;
			countDirs=0;
			SubDirectory = Directory.listFiles();
			sort();
			List = new String[SubDirectory.length+1];
			for (int i=0;i<SubDirectory.length;i++) 
				{
				List[i]="";
				if (SubDirectory[i].isDirectory()) {
					List[i]+="<DIR>\t";
					countDirs++;
				}
				else {
					List[i]+="<FILE>\t";
					byteCount+=SubDirectory[i].length();
					countFiles++;
				}
				List[i]+=SubDirectory[i].getName();
				List[i]+="\t" + new Date(SubDirectory[i].lastModified());
				if (SubDirectory[i].isFile()) List[i]+="\t"+SubDirectory[i].length()+" bytes";
				}
			List[SubDirectory.length]="\t"+countDirs+" folders\n\t"+
				countFiles+" files"+"\t"+byteCount+" bytes";
		return List;
		}
		//method for sort massive on directors, then files
		private void sort(){
			for (int i=0;i<SubDirectory.length;i++)
				if (SubDirectory[i].isFile())
					for (int j=i;j<SubDirectory.length;j++)
						if (SubDirectory[j].isDirectory())
						{ File val = SubDirectory[i];
						SubDirectory[i] = SubDirectory[j];
						SubDirectory[j]=val;
						}
		}
	}
	
	public final class printTreeDirectory extends modelInterface{
		
		//the public part
		public static final String key = "tree";
		
		public String[] doSmth(String[] args){
			tree();
			String[] ListTree = new String[list.size()];
			list.toArray(ListTree);
			return ListTree;
		}
		
		public boolean checkArgs(String[] args){
			if (args.length==1) {
				Directory = workDirectory;
				return true;
			}
			if (args.length==2) {
				Directory = new File(wayAbsoluteRelative.orAbsRel(args[1]));
			return (Directory.exists());
			}
			else return false;
		}
		
		//the private part		
		private LinkedList<String> list;
		private File[] SubDirectory;
		private File Directory;
		
		private void tree(){
			list = new LinkedList<String>();
			contentTree(Directory,0);
		}
		//recursive procedure for finding the contents of a directory
		private void contentTree(File Directory,int Indents)
		{
			String indents="";
			for (int i=0;i<Indents;i++) indents+="     ";
			if (Directory.isFile()){
				indents+=("<FILE> "+Directory.getName()+"\t"+Directory.length()+" bytes");
				list.add(indents);
			}
			else {
			    SubDirectory = Directory.listFiles();
				indents+=("[+]<DIR> "+Directory.getName());
				list.add(indents);
				methodSort();
				for (File SubWay:SubDirectory)
					contentTree(SubWay,Indents+1);
			}
		}
		//sort the massive of files, at first files and then folders
		private void methodSort(){
			ArrayList<File> dirList = new ArrayList<File>();
			ArrayList<File> files = new ArrayList<File>();
			
			for (File subDir:SubDirectory) dirList.add(subDir);
			for (int i=0;i<dirList.size();i++){
				File Obj = dirList.get(i);
				if (Obj.isFile()){
				files.add(Obj);
				dirList.remove(i);
				i--;
				}
			}
			files.addAll(dirList);
			files.toArray(SubDirectory);
		}
	}
	
	public final class findFileInDirectory extends modelInterface{

		//the public part
		public static final String key = "find";
		
		public String[] doSmth(String[] args){
			List = new HashSet<String>();
			procStar();
			find();
			String ListInfo[] = new String[List.size()];
			List.toArray(ListInfo);
			return ListInfo;
		}
		
		public boolean checkArgs(String[] args){
			boolean flag = false;
			
			if (args.length==2){
				Directory = workDirectory;
				Mask = args[1];
				flag = true;
			}
			if (args.length==3){
				Directory = new File(wayAbsoluteRelative.orAbsRel(args[1]));
				if (Directory.exists()){
					Mask = args[2];
					flag = true;
				}
			}
			return flag;
		}
		
		//the private part		
		private File Directory;
		private Pattern p;
		private String Mask;
		private HashSet<String> List;
		
		//recursive procedure to check the contents of a directory
		private void find()
		{
			if (Directory.isFile()) getInfoFile(Directory,Mask);
			else {
				getInfoFile(Directory,Mask);
				File[] SubDirectory = Directory.listFiles();
				for (File SubWay:SubDirectory){
					Directory = SubWay;
					find();
				}
			}
		}
		//method for get the String of information about needed file
		private void getInfoFile(File Object,String Mask){
				String Name = Object.getName();	
				if (accept(Name)) {
					List.add("\n" + Name +"\n" + Object.getPath() + "\n" 
							+ new Date(Object.lastModified())
					+"\t"+Object.length()+" bytes"+"\n");				
				}
		}
		 private boolean accept(String name){ 
			 if(!Mask.equals("")) {
		            p = Pattern.compile(Mask);
		        }
			 if(p.matcher(name).matches()) {
				 return true;
		      }
			 else {
				 return false;
		        }
		 }
		 private void procStar(){
			//processing the "*" in mask
			String newMask = "";
			for (char ch : Mask.toCharArray()){
					if (ch=='*') newMask+=".*";
					else newMask+=ch;
				}
			Mask = newMask;
		 }
	}
	
	public final class copyFile extends modelInterface{
				
		//the public part
		public static final String key = "copy";
		
		public String[] doSmth(String[] args){
			String[] ListInfo = new String[1];
			if (copy()) ListInfo[0] = "File '"+srcFile.getName()+"' is copied";
			else ListInfo[0] = "Unknow error";
			return ListInfo;
		}
		
		public boolean checkArgs(String[] args){
			boolean flag = false;
			if (args.length==3){
				srcFile = new File(wayAbsoluteRelative.orAbsRel(args[1]));
				dstFile = new File(wayAbsoluteRelative.orAbsRel(args[2]));
				if (!srcFile.exists()) flag = false;
				if(srcFile.exists()&&!dstFile.exists()){
					try{
						flag = (dstFile.createNewFile());
					}
					catch (IOException e){flag = false;}
				}
				if (srcFile.exists()&&dstFile.exists()) flag = true;
				else flag = false;
				return flag;
			}
			return false;
		}
		
		//the private part	
		private File srcFile;
		private File dstFile;
		
		private boolean copy(){		
			try{		
				InputStream in = new FileInputStream(srcFile);
                OutputStream out = new FileOutputStream(dstFile);
                byte[] buf = new byte[1024];
                int length;
                while ((length = in.read(buf)) > 0){
                out.write(buf, 0, length);
                }
                in.close();
                out.close();
                return true;
			}
			catch (IOException e)
			{
				return false;
			}
		}
	}
	
	public final class moveFile extends modelInterface{
				
		//the public part
		public static final String key = "move";
		
		public String[] doSmth(String[] args){
			ListInfo = copyObj.doSmth(args);
			ListInfo = delObj.doSmth(args);
			if (ListInfo!=null) ListInfo[0] = "File is moved";
			return ListInfo;
		}
		public boolean checkArgs(String[] args){
			boolean flag = false;
			copyObj = new copyFile();
			delObj = new deleteDir();
			flag = copyObj.checkArgs(args);
			flag = delObj.checkArgs(args);
			return flag;
		}
		
		//the private part	
		copyFile copyObj;
		deleteDir delObj;
		String[] ListInfo;
	}

	public final class makeDirectory extends modelInterface{
				
		//the public part
		public static final String key = "mkdir";
		
		public String[] doSmth(String[] args){
			String[] ListInfo = new String[1];
			if (Directory.mkdir()) ListInfo[0] = "Directory '"+Directory.getName()+"' is create";
			else ListInfo[0] = "Unknow error.";
			return ListInfo;
		}
		
		public boolean checkArgs(String[] args){
			if (args.length==1) return false;
			else {
				Directory = new File(wayAbsoluteRelative.orAbsRel(args[1]));
				return (!Directory.exists());
			}
		}
		
		//the private part
		private File Directory;
	}

	public final class deleteDir extends modelInterface{
		
		//the public part
		public static final String key = "del";
		
		public String[] doSmth(String[] args){
			String[] ListInfo = new String[1];
			if (del()) ListInfo[0] = "File "+Directory.getName()+" is deleted";
			else ListInfo[0] = "Unknow error";
			return ListInfo;
		}
		
		public boolean checkArgs(String[] args){
			if (args.length==1) return false;
			else {	
				Directory = new File(wayAbsoluteRelative.orAbsRel(args[1]));
				return (Directory.exists());
			}
		}
		
		//the private part
		private File Directory;
		private File[] SubDirectory;
		
		private boolean del(){
				if (Directory.isFile()) return Directory.delete();
				else{
				while (!Directory.delete()){
					delDir(Directory);
				}
				return true;
				}
			}
			
		//recursive procedure for delete the all files in directory
		private void delDir(File Directory)
			{
				if (Directory.isFile()) Directory.delete();
				else {
					if (Directory.delete()){}
					else{
				    SubDirectory = Directory.listFiles();
					for (File SubWay:SubDirectory)
						delDir(SubWay);}
				}
		}
	}
	
	public final class changeWorkDirectory extends modelInterface{
		
		//the public part
		public static final String key = "cd";
		
		public String[] doSmth(String[] args){
			String[] newWD = new String[1];
			Controller Object = new Controller();
			Object.changeWD(workDirectory);
			newWD[0] ="";
			return newWD;
		}
		
		public boolean checkArgs(String[] args){
			if (args.length==1) return false;
			else { 
			workDirectory = new File(wayAbsoluteRelative.orAbsRel(args[1]));
			return (workDirectory.exists());
			}
		}
	}

	public final class endOfWork extends modelInterface{
    	
		//the public part
		public static final String key = "exit";
    	
    	public String[] doSmth(String[] args){
    		System.exit(0);
    		return null;
    	}
    	
    	public boolean checkArgs(String args[]){
    		return true;
    	}
    }

}


